---
title: Middleware
description: Run code before and after your API handlers
---

## What is Middleware?

Middleware runs before your API handler. Use it for authentication, logging, error handling, or any logic that applies to multiple endpoints.

---

## How It Works

A middleware is a function that receives three arguments:

```typescript
middleware(req, ctx, next)
```

- **req** - The incoming request (same as handler)
- **ctx** - The context object (same as handler)  
- **next()** - Call this to continue to the handler

If you don't call `next()`, the request stops. The handler never runs.

---

## Simple Example

<Tabs items={['TypeScript', 'JavaScript', 'Python']}>
  <Tab value="TypeScript">
    ```typescript
    import { ApiMiddleware } from 'motia'

    const authMiddleware: ApiMiddleware = async (req, ctx, next) => {
      if (!req.headers.authorization) {
        return { status: 401, body: { error: 'Unauthorized' } }
      }
      return next()
    }

    export const config = {
      name: 'ProtectedEndpoint',
      type: 'api',
      path: '/protected',
      method: 'GET',
      middleware: [authMiddleware]
    }

    export const handler = async (req, ctx) => {
      return { status: 200, body: { message: 'Success' } }
    }
    ```
  </Tab>
  <Tab value="JavaScript">
    ```javascript
    const authMiddleware = async (req, ctx, next) => {
      if (!req.headers.authorization) {
        return { status: 401, body: { error: 'Unauthorized' } }
      }
      return next()
    }

    const config = {
      name: 'ProtectedEndpoint',
      type: 'api',
      path: '/protected',
      method: 'GET',
      middleware: [authMiddleware]
    }

    const handler = async (req, ctx) => {
      return { status: 200, body: { message: 'Success' } }
    }

    module.exports = { config, handler }
    ```
  </Tab>
  <Tab value="Python">
    ```python
    async def auth_middleware(req, context, next_fn):
        if not req.get("headers", {}).get("authorization"):
            return {"status": 401, "body": {"error": "Unauthorized"}}
        return await next_fn()

    config = {
        "name": "ProtectedEndpoint",
        "type": "api",
        "path": "/protected",
        "method": "GET",
        "middleware": [auth_middleware]
    }

    async def handler(req, context):
        return {"status": 200, "body": {"message": "Success"}}
    ```
  </Tab>
</Tabs>

---

## Execution Order

Middleware runs in the order you list them:

```typescript
export const config = {
  name: 'MyEndpoint',
  type: 'api',
  path: '/endpoint',
  method: 'POST',
  middleware: [
    loggingMiddleware,  // Runs first
    authMiddleware,     // Runs second  
    errorMiddleware     // Runs third
  ]
}
```

---

## Modifying Responses

Await `next()` to get the response, then modify it:

<Tabs items={['TypeScript', 'JavaScript', 'Python']}>
  <Tab value="TypeScript">
    ```typescript
    const addHeadersMiddleware = async (req, ctx, next) => {
      const response = await next()
      
      return {
        ...response,
        headers: {
          ...response.headers,
          'X-Request-Id': ctx.traceId
        }
      }
    }
    ```
  </Tab>
  <Tab value="JavaScript">
    ```javascript
    const addHeadersMiddleware = async (req, ctx, next) => {
      const response = await next()
      
      return {
        ...response,
        headers: {
          ...response.headers,
          'X-Request-Id': ctx.traceId
        }
      }
    }
    ```
  </Tab>
  <Tab value="Python">
    ```python
    async def add_headers_middleware(req, context, next_fn):
        response = await next_fn()
        
        headers = response.get("headers", {})
        headers["X-Request-Id"] = context.trace_id
        
        return {**response, "headers": headers}
    ```
  </Tab>
</Tabs>

---

## Passing Data to Handlers

Middleware can attach data to the `req` object, making it available to your handler. This is perfect for authentication—verify the user once in middleware, then use their details in the handler.

<Tabs items={['TypeScript', 'JavaScript', 'Python']}>
  <Tab value="TypeScript">
    First, extend the request type in `api.d.ts`:

    ```typescript
    // api.d.ts
    import 'motia'

    declare module 'motia' {
      interface ApiRequest {
        user?: { id: string; role: string }
      }
    }
    ```

    Then attach the data in your middleware:

    ```typescript
    const authMiddleware: ApiMiddleware = async (req, ctx, next) => {
      // Verify token...
      req.user = { id: '123', role: 'admin' }
      return next()
    }
    ```

    Now use it in your handler:

    ```typescript
    export const handler = async (req, ctx) => {
      // req.user is typed and ready to use
      if (req.user?.role === 'admin') {
        return { status: 200, body: { message: 'Welcome Admin' } }
      }
      return { status: 403, body: { error: 'Forbidden' } }
    }
    ```
  </Tab>
  <Tab value="JavaScript">
    Attach data to `req` in middleware:

    ```javascript
    const authMiddleware = async (req, ctx, next) => {
      req.user = { id: '123', role: 'admin' }
      return next()
    }
    ```

    Access it in your handler:

    ```javascript
    const handler = async (req, ctx) => {
      const { user } = req
      return { status: 200, body: { user } }
    }
    ```
  </Tab>
  <Tab value="Python">
    Add data to the `req` dictionary:

    ```python
    async def auth_middleware(req, context, next_fn):
        req["user"] = {"id": "123", "role": "admin"}
        return await next_fn()

    async def handler(req, context):
        user = req.get("user")
        return {"status": 200, "body": {"user": user}}
    ```
  </Tab>
</Tabs>

> **Learn more:** Check out the [Middleware Auth Handler Example](https://github.com/MotiaDev/motia-examples/tree/main/examples/middleware-auth-handler-example) to see a complete project with JWT validation and type safety.

---

## Error Handling

Catch errors from handlers:

<Tabs items={['TypeScript', 'JavaScript', 'Python']}>
  <Tab value="TypeScript">
    ```typescript
    import { ZodError } from 'zod'

    const errorMiddleware = async (req, ctx, next) => {
      try {
        return await next()
      } catch (error: any) {
        if (error instanceof ZodError) {
          ctx.logger.error('Validation error', { errors: error.errors })
          return { status: 400, body: { error: 'Validation failed' } }
        }

        ctx.logger.error('Unexpected error', { error: error.message })
        return { status: 500, body: { error: 'Internal server error' } }
      }
    }
    ```
  </Tab>
  <Tab value="JavaScript">
    ```javascript
    const { ZodError } = require('zod')

    const errorMiddleware = async (req, ctx, next) => {
      try {
        return await next()
      } catch (error) {
        if (error instanceof ZodError) {
          ctx.logger.error('Validation error', { errors: error.errors })
          return { status: 400, body: { error: 'Validation failed' } }
        }

        ctx.logger.error('Unexpected error', { error: error.message })
        return { status: 500, body: { error: 'Internal server error' } }
      }
    }
    ```
  </Tab>
  <Tab value="Python">
    ```python
    async def error_middleware(req, context, next_fn):
        try:
            return await next_fn()
        except ValidationError as e:
            context.logger.error("Validation error", {"errors": str(e)})
            return {"status": 400, "body": {"error": "Validation failed"}}
        except Exception as e:
            context.logger.error("Unexpected error", {"error": str(e)})
            return {"status": 500, "body": {"error": "Internal server error"}}
    ```
  </Tab>
</Tabs>

---

## Reusing Middleware

Create middleware files in a shared location:

```typescript title="middlewares/core.middleware.ts"
export const coreMiddleware = async (req, ctx, next) => {
  try {
    return await next()
  } catch (error) {
    ctx.logger.error('Error', { error })
    return { status: 500, body: { error: 'Internal server error' } }
  }
}
```

Import and use across steps:

```typescript title="steps/user.step.ts"
import { coreMiddleware } from '../middlewares/core.middleware'

export const config = {
  name: 'GetUser',
  type: 'api',
  path: '/users/:id',
  method: 'GET',
  middleware: [coreMiddleware]
}
```

---

## What's Next?

<Cards>
  <Card href="/docs/concepts/steps#triggers" title="Triggers">
    Learn more about Triggers
  </Card>
  
  <Card href="/docs/development-guide/testing" title="Testing">
    Learn more about testing your Motia Steps
  </Card>
</Cards>
